Skip to main content

FAQ

1) I have market data stored in a stream with a package header format. How can I select all trades and/or L1 quotes from this stream?

Answer

The package header format stores all data in an array of polymorphic objects called entries. Use array join construction to 'unfold' this array into separate entry messages, and filter them by type (TradeEntry, L1Entry, etc).

--Query example
SELECT entry.exchangeId as 'exchangeId', entry.price as 'price', entry.size as 'size'
TYPE "deltix.timebase.api.messages.TradeMessage"
FROM "COINBASE"
ARRAY JOIN entries AS entry
WHERE entry IS TradeEntry
2) When I select entry.side, I get an error.

Question

I get an error related to the entry.side field when I try selecting trades from the stream.

Error message: "Illegal type in: ENTRY.SIDE; Types should be equal".

Answer

The elements of the entries array can be of various types: TradeEntry, L1Entry, L2EntryNew, and L2EntryUpdate. There may potentially be an ambiguity with the side field because it can have differing types, for example, AggressorSide for TradeEntry.side and QuoteSide for others.

To resolve this, you can cast the entries array to a fixed TradeEntry array type using the syntax array(TradeEntry):

SELECT 
entry.exchangeId, entry.price, entry.size, entry.side
TYPE "deltix.timebase.api.messages.TradeMessage"
FROM "binance"
ARRAY JOIN (entries AS array(TradeEntry)) AS entry
WHERE entry != null
3) I have market data stored in a stream with a Package Header format. How can I select all trades as a TradeMessage and L1 entries as a BestBidOfferMessage?

Answer

The Package Header format stores all data in an array of polymorphic objects called entries. Construct two queries that select TradeEntry and BestBidOfferMessage separately as we did in the first question. For that, use the array join construction to 'unfold' the entries array into separate entry messages and filter them by type (TradeEntry and L1Entry). Then, use the keyword TYPE to name the output message types as standard TimeBase API Messages TradeMessage and BestBidOfferMessage. And finally, use UNION to merge the queries into a polymorphic output:

SELECT 
entry.exchangeId AS 'exchangeId',
entry.price AS 'price',
entry.size AS 'size'
TYPE "deltix.timebase.api.messages.TradeMessage"
FROM "BITFINEX"
ARRAY JOIN entries AS entry
WHERE entry IS TradeEntry
UNION
SELECT
(entry.exchangeId if entry.side == BID) AS 'bidExchangeId',
(entry.price if entry.side == BID) AS 'bidPrice',
(entry.size if entry.side == BID) AS 'bidSize',
(entry.exchangeId if entry.side == ASK) AS 'offerExchangeId',
(entry.price if entry.side == ASK) AS 'offerPrice',
(entry.size if entry.side == ASK) AS 'offerSize'
TYPE "deltix.timebase.api.messages.BestBidOfferMessage"
FROM "BITFINEX"
ARRAY JOIN (entries AS array(L1Entry)) AS entry
WHERE entry != null

As an alternative, use the RECORD keyword instead of UNION:

WITH
entry as TradeEntry as trade,
entry as L1Entry as l1
SELECT
RECORD
trade.exchangeId FIELD 'exchangeId',
trade.price FIELD 'price',
trade.size FIELD 'size'
TYPE "deltix.timebase.api.messages.TradeMessage" WHEN entry IS TradeEntry
RECORD
(l1.exchangeId if l1.side == BID) FIELD 'bidExchangeId',
(l1.price if l1.side == BID) FIELD 'bidPrice',
(l1.size if l1.side == BID) FIELD 'bidSize',
(l1.exchangeId if l1.side == ASK) FIELD 'offerExchangeId',
(l1.price if l1.side == ASK) FIELD 'offerPrice',
(l1.size if l1.side == ASK) FIELD 'offerSize'
TYPE "deltix.timebase.api.messages.BestBidOfferMessage" WHEN entry is L1Entry
FROM "BITFINEX"
ARRAY JOIN entries AS entry
4) How can I fill gaps with the last available value?

Question

I am trying to calculate a spread for L1 market data. The problem is that I cannot do it by taking the difference between bids and asks because quotes in my data have different timestamps and there are also missing price values here and there. I want to resolve this by filling the missing price values with the last available data. How do I do that?

SELECT
entry[side == ASK].price AS askPrice,
entry[side == BID].price AS bidPrice
FROM "BINANCE"
ARRAY JOIN (entries AS array(L1Entry)) AS entry
WHERE entry != null AND symbol == 'BTC/USDT'

Answer

Use the lastNotNull{}(field) function with the SELECT RUNNING keyword construction. You need a RUNNING keyword to avoid calculating results for the entire stream.

SELECT RUNNING
lastNotNull{}(entry[side == ASK].price) AS askPrice,
lastNotNull{}(entry[side == BID].price) AS bidPrice,
askPrice - bidPrice AS spread
FROM "BINANCE"
ARRAY JOIN (entries AS array(L1Entry)) AS entry
WHERE entry != null AND symbol == 'BTC/USDT'
5) I want to build an order book snapshot for each message in the stream.

Answer

Use the orderBook{}() function. This function builds an order book by combining the snapshot with incremental updates of data stored in the stream. It then returns the current state of the order book as a snapshot in the package header format.

WITH
orderbook{maxDepth: 20}(this.packageType, this.entries) AS book
SELECT RUNNING
book AS entries, PERIODICAL_SNAPSHOT AS packageType
TYPE "deltix.timebase.api.messages.universal.PackageHeader"
FROM "BINANCE"
WHERE symbol == 'BTC/USDT' and size(book) > 0
6) How can I flatten order book entries?

Question

I have an order book built from a package header stream. How can I take the entries array and represent its elements as individual messages?

For example, I want to take the top 2 levels of an order book and flatten them, so each entries array element is projected into a separate message. I would expect the result to look like this:

symbol, timestamp, exchangeId, price, size, level, side
BTCTUSD, 2023-04-25T13:07:30.758Z, BINANCE, 27441.29, 0.9988, 0, ASK
BTCTUSD, 2023-04-25T13:07:30.758Z, BINANCE, 27441.58, 0.12762, 1, ASK
BTCTUSD, 2023-04-25T13:07:30.758Z, BINANCE, 27441.28, 0.00026, 0, BID
BTCTUSD, 2023-04-25T13:07:30.758Z, BINANCE, 27441.21, 0.0004, 1, BID

Answer

Use this query:

WITH
orderbook{maxDepth: 2}(this.packageType, this.entries[not this is TradeEntry]) AS book
SELECT RUNNING
book_entry.exchangeId AS 'exchangeId',
book_entry.price AS 'price',
book_entry.size AS 'size',
book_entry.level AS 'level',
book_entry.side AS 'side'
FROM "BINANCE"
ARRAY JOIN (book AS array(L2EntryNew)) AS book_entry
WHERE symbol == 'BTCTUSD'
7) What query can I use to count messages by day?

Answer

Use the over time construction to aggregate values:

SELECT count{}() FROM "BINANCE" 
OVER TIME(1d)
8) How can I calculate a message rate over 1 second intervals or 1 minute intervals for the universal market data format?

Answer

To calculate rates in a single query, use the UNION construction:

SELECT count{}() AS 'seconds_rate' FROM "COINBASE" 
OVER TIME(1s)
UNION
SELECT count{}() AS 'minutes_rate' FROM "COINBASE"
OVER TIME(1m)

Refer to UNION to learn more.

9) How can I calculate the maximum size of the entries array in PackageHeader snapshots?

Answer

Use the MAX{}() and SIZE() funtions:

SELECT max{}(size(entries)) FROM "COINBASE" 
where packageType == PERIODICAL_SNAPSHOT
10) In a TimeBase stream containing bars for the two symbols BTCUSDT and ETHUSDT, is it possible to select the prices of BTCUSDT and ETHUSDT in a single row and calculate the difference between them?

Answer

Use the if operator to filter the price for the specified symbol. Then, utilize the lastNotNull{}() function to fill any gaps in the results.

WITH
lastNotNull{}(closeAsk if symbol == 'BTCUSDT') AS 'price1',
lastNotNull{}(closeAsk if symbol == 'ETHUSDT') AS 'price2'
SELECT RUNNING
price1, price2, price1 - price2 AS diff
FROM Bars
WHERE symbol IN ('BTCUSDT', 'ETHUSDT') and price1 != null and price2 != null
11) How can I create a query to retrieve prices for the same symbol that's available on two exchanges? For example, I want to find the price difference of BTC/USDT between BINANCE and BITFINEX (spread).

Answer

To achieve this, construct two order books for each exchange utilizing the OrderBook{}() function. Use the UNION keyword to select from both streams. First, filter entries from the stream for each exchange using a predicate like ((THIS.entries[exchangeId == 'BINANCE']) as 'BinanceEntries'). Then, pass the filtered data into the OrderBook{}() function and fill any gaps using lastNotNull{}(). Finally, calculate the spread from the top of the order books.

with
(THIS.entries[exchangeId == 'BINANCE']) as 'BinanceEntries',
(THIS.entries[exchangeId == 'BITFINEX']) as 'BitfinexEntries',
lastNotNull{}(ORDERBOOK{}(THIS.packageType, BinanceEntries)) AS array(L2EntryNew) AS 'BinanceBook',
lastNotNull{}(ORDERBOOK{}(THIS.packageType, BitfinexEntries)) AS array(L2EntryNew) AS 'BitfinexBook',
(BinanceBook[level==0 and side == BID].price[0] + BinanceBook[level==0 and side == ASK].price[0]) / 2 AS 'BinancePrice',
(BitfinexBook[level==0 and side == BID].price[0] + BitfinexBook[level==0 and side == ASK].price[0]) / 2 AS 'BitfinexPrice',
BinancePrice - BitfinexPrice AS 'Spread'
SELECT
BinancePrice, BitfinexPrice, Spread
FROM ("BITFINEX" UNION "BINANCE")
over time(100ms)
WHERE symbol == 'BTC/USDT' and BinanceBook != null and BitfinexBook != null
12) How can I retrieve the latest best prices for symbols present in two separate streams?

Answer

WITH
(THIS.entries if symbol == 'BTCUSDT') as 'CashEntries',
(THIS.entries if symbol == 'BTCPC-T') as 'FutureEntries',
lastNotNull{}(ORDERBOOK{}(THIS.packageType, CashEntries)) as array(L2EntryNew) as 'CashBook',
lastNotNull{}(ORDERBOOK{}(THIS.packageType, FutureEntries)) as array(L2EntryNew) as 'FutureBook',
CashBook[level==0 and side == BID].price[0] as 'CASH',
FutureBook[level==0 and side == BID].price[0] as 'FUTURE'
SELECT
FUTURE, CASH
FROM ("BINANCE" UNION "BINANCEFUT")
over time(100ms)
WHERE symbol IN ('BTCUSDT', 'BTCPC-T') and CashBook != null and FutureBook != null
13) Is there a windowed standard deviation function available in QQL, equivalent to the Pandas operation df['price'].rolling('60s').std()?

Yes, use the statWindow function with the initial timePeriod parameter:

SELECT RUNNING statWindow{timePeriod:60s}(volume).standardDeviation
FROM "1sec.bars"
WHERE symbol == 'AAPL'
14) How can I calculate the difference between the current and previous volume values in my bars stream?

Use the window function with a fixed period of size 2:

WITH
window{period:2}(volume) as w
SELECT RUNNING w[1] - w[0]
FROM "bars"
WHERE symbol == 'AAPL'
15) How can I select all TimeBase streams and their types?

To retrieve a list of all streams along with their associated metadata, utilize the streams() function. Afterwards, you can iterate through the streams using the ARRAY JOIN clause to extract the specific information you require.

SELECT s.key AS key, s.topTypes[not isAbstract].name AS types
ARRAY JOIN streams() AS s
16) How do I fetch symbols from a securities stream?

To retrieve symbols from a securities stream, use the symbols() function in the following manner:

SELECT s
ARRAY JOIN symbols('securities') AS s
17) How can I select all available functions supported by QQL?

To obtain a list of all available functions in QQL, you can make use of the stateless_functions() and stateful_functions() functions, for example:

SELECT f.id, f.arguments.name, f.arguments.dataType.baseName
ARRAY JOIN stateless_functions() AS f
18) I need to map my result set to a standard BarMessage (from TimeBase API Messages).

To set the type name of the query result, use the keyword TYPE.

SELECT open, close, high, low, volume TYPE "deltix.timebase.api.messages.BarMessage"
FROM MyStream
19) How do I change the precision of the receiveTimestamp and originalTimestamp fields in my stream to nanosecond?

Use the ALTER STREAM query:

ALTER STREAM BINANCE
ALTER CLASS "deltix.timebase.api.messages.MarketMessage" (
ALTER FIELD "receiveTimestamp" SET encoding NANOSECOND;
ALTER FIELD "originalTimestamp" SET encoding NANOSECOND
)
CONFIRM CONVERT_DATA
20) How can I count the number of distinct symbols in a securities stream?

Utilize COLLECT_UNIQUE function:

SELECT SIZE(COLLECT_UNIQUE{}(symbol)) FROM "securities" 

Since 5.6.78+ you can use Inner select:

SELECT COUNT{}() FROM (SELECT DISTINCT symbol FROM "securities")
21) I have an "ember-messages" stream. How to calculate sum of trade quantities by each instrument for each day (for partially and completely filled orders), given that the trading day starts at 17:00 UTC.

Since 5.6.78+ QQL supports over time with offset:

SELECT
SUM{}(tradeQuantity * -1 if orderEvent:Side == SELL else tradeQuantity)
FROM "ember-messages"
OVER Time(1d, 17h)
WHERE orderStatus IN (COMPLETELY_FILLED, PARTIALLY_FILLED)
GROUP BY symbol